﻿#pragma once

#ifndef DISABLE_SB_CODE
#include "util/box_std_allocator.hh"
#include "util/object_pool.hh"
#endif

#include <atomic>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <ostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

typedef struct _zhandle zhandle_t;

namespace kratos {
namespace service {

struct FileStream;

// Zookeeper客户端
class ZookeeperClient {
private:
#ifndef DISABLE_SB_CODE
  using KeyCache = PoolUnorederedMap<std::string, std::string>;
  using ChildCache = PoolUnorederedMap<std::string, std::list<std::string>>;
  using KeySet = PoolUnorderedSet<std::string>;
#else
  using KeyCache = std::unordered_map<std::string, std::string>;
  using ChildCache = std::unordered_map<std::string, std::list<std::string>>;
  using KeySet = std::unordered_set<std::string>;
#endif
  zhandle_t *zhandle_{nullptr};          ///< Zookeeper实例
  KeyCache key_cache_;                   ///< 键缓存
  std::mutex key_cache_mutex_;           ///< 键缓存锁
  ChildCache child_cache_;               ///< 子对象缓存
  std::mutex child_cache_mutex_;         ///< 子对象缓存锁
  std::atomic_bool is_connected_{false}; ///< 连接标志
  std::string host_;                     ///< 主机地址，多个
  int timeout_{1000};                    ///< 连接超时
  KeySet my_keys_;                       ///< 启动后注册的键
  KeySet changed_keys_;                  ///< 改变的键
  bool dirty_flag_{false};               ///< 脏标记
  std::string version_;                  ///< 版本号

public:
  /**
   * 构造.
   *
   * \param version 版本号
   */
  ZookeeperClient(const std::string &version);
  /**
   * 析构.
   *
   */
  ~ZookeeperClient();
  /**
   * 检测关注的路径是否有变化
   */
  auto is_changed() -> bool;
  /**
   * 获取发生变化的路径
   * \param [out] path 发生变化的路径
   */
  auto get_changed_path(std::vector<std::string> &path) -> void;
  // 连接zookeeper服务
  // @param host 主机地址
  // @param timeout 连接超时(毫秒)
  // @retval true 成功
  // @retval false 失败
  auto connect(const std::string &host, int timeout) -> bool;
  // 断开与远程主机连接
  // @retval true 成功
  // @retval false 失败
  auto disconnect() -> bool;
  // 取得键对应的值
  // @param key 键
  // @param value 值
  // @retval true 成功
  // @retval false 失败
  auto get(const std::string &key, std::string &value) -> bool;
  // 取得键（目录）对应的子对象
  // @param key 键
  // @param children 子对象名列表
  // @retval true 成功
  // @retval false 失败
  auto get_children(const std::string &key, std::list<std::string> &children)
      -> bool;
  // 取得键（目录）对应的子对象
  // @param key 键
  // @param children 子对象名列表
  // @retval true 成功
  // @retval false 失败
  auto get_children(const std::string &key, std::vector<std::string> &children)
      -> bool;
  /**
   * 获取键（目录）对应的子对象数量.
   *
   * \param key 键
   * \return 子对象数量
   */
  auto get_children_count(const std::string &key) -> std::size_t;
  // 设置键值
  // @param key 键
  // @param value 值
  // @retval true 成功
  // @retval false 失败
  auto set(const std::string &key, const std::string &value) -> bool;
  // 建立路径，但不设置观察事件
  // @param key 键
  // @param value 值
  // @retval true 成功
  // @retval false 失败
  auto set_path(const std::string &key, const std::string &value) -> bool;
  // 删除没有子对象的键
  // @param key 键
  // @retval true 成功
  // @retval false 失败
  auto remove(const std::string &key) -> bool;
  // 判断键是否存在
  // @param key 键
  // @retval true 是
  // @retval false 否
  auto exists(const std::string &key) -> bool;
  // 删除缓存键值对
  // @param key 键
  // @param value 值
  auto remove_cache(const std::string &key, const std::string &value) -> void;
  // 判断是否已经连接到服务器
  // @retval true 是
  // @retval false 否
  auto is_connected() -> bool;
  // 使用上次connect成功的参数进行重连
  // @retval true 成功
  // @retval false 失败
  auto reconnect() -> bool;
  // 输出调试信息
  auto dump(std::ostream &os) -> void;

private:
  static void init_watch_fn(zhandle_t *zh, int type, int state,
                            const char *path, void *context);
  static void wget_watch_fn(zhandle_t *zh, int type, int state,
                            const char *path, void *context);
  static void wexists_watch_fn(zhandle_t *zh, int type, int state,
                               const char *path, void *context);
  static void wget_children_watch_fn(zhandle_t *zh, int type, int state,
                                     const char *path, void *context);

  bool check_host(const std::string &host);
  auto get_versioned_path(const std::string &key) -> std::string;
};

} // namespace service
} // namespace kratos
